#include "../include/Engine.hpp"
#include "../include/Library.hpp"
#include "../include/error/Exception.hpp"

#include "../include/graphics/gl/GLRenderingCore.hpp"
#include "../include/graphics/gl/GLTexture.hpp"
#include "../include/graphics/Color.hpp"
#include "../include/GLLibs.hpp"
using namespace klas;
using namespace graphics::colors;

graphics::RenderingCore* Engine::renderingCore;
uint8 Engine::graphicsLibrary = library::BASE;


void initGL(const graphics::Color& col, const math::Vector2<int>& dim, bool fullscreen, string title)
{
  // start SDL
  if(SDL_Init(SDL_INIT_EVERYTHING) == -1)
    throw error::Exception("Engine::init failed on SDL_Init(SDL_INIT_EVERYTHING)");

  atexit(SDL_Quit);

  // set the title
  SDL_WM_SetCaption(title.c_str(), NULL);

  // create the window
  Uint32 flags = SDL_OPENGL;
  if(fullscreen)
    flags |= SDL_FULLSCREEN;
  SDL_SetVideoMode(dim.x, dim.y, 32, flags);

  glClearDepth(1.0f);
  glClearColor(GLclampf(col.r), GLclampf(col.g), GLclampf(col.b), GLclampf(col.a));

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(0.0f, dim.x, 0.0f, dim.y, -15, 15);

  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}


void Engine::init(uint8 system, const math::Vector2<int>& dim, string title, const graphics::Color& col, bool fullscreen)
{
  graphicsLibrary = system;

  switch(system)
  {
  // Only OpenGL is implemented, so set that up.
  case library::GL:
  {
    // setup DevIL (or OpenIL)
    ilInit();
    iluInit();

    // setup window and renderer
    initGL(col, dim, fullscreen, title);
    renderingCore = new graphics::gl::GLRenderingCore(1000);
    break;
  }
  // Throw errors on everything else
  case library::DX:
    throw error::Exception("Engine::init was unable to create the DirectX rendering system.");
    break;
  case library::SF:
    throw error::Exception("Engine::init was unable to create the Software rendering system.");
    break;
  default:
    throw error::Exception("Engine::init was given an unrecognized rendering system.");
    break;
  }
    
}

void Engine::render(const math::Rectd& cam, bool clearCache)
{
  renderingCore->render(cam);
  if(clearCache)
    renderingCore->clearCache();
}

graphics::Texture* Engine::newTexture()
{
  switch(graphicsLibrary)
  {
  case library::GL:
    return new graphics::gl::GLTexture();
   
  case library::DX:
    throw error::Exception("Engine::newTexture was unable to create the DirectX texture.");
    break;
  case library::SF:
    throw error::Exception("Engine::newTexture was unable to create the Software texture.");
    break;
  case library::BASE:
    throw error::Exception("Engine::newTexture was unable to create a texture because init was never called.");
    break;
  default:
    throw error::Exception("Engine::newTexture was given an unrecognized rendering system.");
    break;
  }
}

graphics::Texture* Engine::newTexture(string filename)
{
  switch(graphicsLibrary)
  {
  case library::GL:
  {
    return new graphics::gl::GLTexture(filename);
    break;
  }

  case library::DX:
    throw error::Exception("Engine::newTexture was unable to create the DirectX texture.");
    break;
  case library::SF:
    throw error::Exception("Engine::newTexture was unable to create the Software texture.");
    break;
  case library::BASE:
    throw error::Exception("Engine::newTexture was unable to create a texture because init was never called.");
    break;
  default:
    throw error::Exception("Engine::newTexture was given an unrecognized rendering system.");
    break;
  }
}